package com.example.goodskilldemo.service.impl;

import com.example.goodskilldemo.constants.Constants;
import com.example.goodskilldemo.constants.SeckillStatusEnum;
import com.example.goodskilldemo.dao.Expand_GoodsInfoMapper;
import com.example.goodskilldemo.dao.Expand_GoodsSeckillMapper;
import com.example.goodskilldemo.dao.Expand_GoodsSeckillSuccessMapper;
import com.example.goodskilldemo.http.Result;
import com.example.goodskilldemo.http.ResultGenerator;
import com.example.goodskilldemo.model.GoodsInfo;
import com.example.goodskilldemo.model.GoodsSeckill;
import com.example.goodskilldemo.model.GoodsSeckillSuccess;
import com.example.goodskilldemo.model.custom.ExposerVO;
import com.example.goodskilldemo.model.custom.SeckillGoodsVO;
import com.example.goodskilldemo.model.custom.SeckillSuccessVO;
import com.example.goodskilldemo.service.IGoodsSeckillService;
import com.example.goodskilldemo.utils.MD5Utils;
import com.example.goodskilldemo.utils.RedisCache;
import com.google.common.util.concurrent.RateLimiter;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class GoodsSeckillServiceImpl implements IGoodsSeckillService {

    //使用令牌桶限流
    //创建一个稳定输出令牌的RateLimiter，保证了平均每秒不超过permitsPerSecond个请求
    private static final RateLimiter rateLimiter = RateLimiter.create(10);

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private Expand_GoodsSeckillMapper seckillMapper;

    @Autowired
    private Expand_GoodsInfoMapper goodsInfoMapper;

    @Autowired
    private Expand_GoodsSeckillSuccessMapper seckillSuccessMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Result selectSeckillGoodsList() {
        // 直接返回配置的秒杀商品列表
        // 不返回商品id，每配置一条秒杀数据，就生成一个唯一的秒杀id和发起秒杀的事件id，根据秒杀id去访问详情页
        List<SeckillGoodsVO> seckillGoodsVOS = redisCache.getCacheObject(Constants.SECKILL_GOODS_LIST);
        if (CollectionUtils.isEmpty(seckillGoodsVOS)) {
            List<GoodsSeckill> seckillGoodsListFromDb = seckillMapper.selectHomeSeckillListPage();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分");
            seckillGoodsVOS = seckillGoodsListFromDb.stream().map(goodsSeckill -> {
                SeckillGoodsVO seckillGoodsVO = new SeckillGoodsVO();
                BeanUtils.copyProperties(goodsSeckill, seckillGoodsVO);

                //校验商品是否存在
                GoodsInfo goodsInfo = goodsInfoMapper.selectByPrimaryKey(goodsSeckill.getGoodsId());
                if (goodsInfo == null) {
                    return null;
                }
                seckillGoodsVO.setGoodName(goodsInfo.getGoodsName());
                seckillGoodsVO.setGoodsCoverImg(goodsInfo.getGoodsCoverImg());
                seckillGoodsVO.setSeckillPrice(goodsInfo.getSellingPrice());

                Date seckillBeginDate = seckillGoodsVO.getSeckillBegin();
                Date seckillEndDate = seckillGoodsVO.getSeckillEnd();
                String seckillBeginStr = sdf.format(seckillBeginDate);
                String seckillEndStr = sdf.format(seckillEndDate);
                seckillGoodsVO.setSeckillBeginTime(seckillBeginStr);
                seckillGoodsVO.setSeckillEndTime(seckillEndStr);

                return seckillGoodsVO;
            }).filter(seckillGoodsVO -> seckillGoodsVO != null).collect(Collectors.toList());

        }
        return ResultGenerator.genSuccessResult(seckillGoodsVOS);
    }

    @Override
    public Result exposerUrl(Long seckillId) {
        SeckillGoodsVO seckillGoodsVO = redisCache.getCacheObject(Constants.SECKILL_GOODS_DETAIL + seckillId);
        if (seckillGoodsVO == null) {
            return ResultGenerator.genFailResult("请先预热秒杀商品");
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分");
        //校验时间
        Date seckillBegin = seckillGoodsVO.getSeckillBegin();
        Date seckillEnd = seckillGoodsVO.getSeckillEnd();

        //系统当前时间
        Date nowDate = new Date();
        if (nowDate.getTime() < seckillBegin.getTime()) {//秒杀还未开始
            ExposerVO exposerVO = new ExposerVO(SeckillStatusEnum.NOT_START, seckillId, sdf.format(nowDate), sdf.format(seckillBegin), sdf.format(seckillEnd));
            return ResultGenerator.genFailResult(exposerVO);
        }
        if (nowDate.getTime() > seckillEnd.getTime()) {//秒杀已经结束
            ExposerVO exposerVO = new ExposerVO(SeckillStatusEnum.ALREADY_END, seckillId, sdf.format(nowDate), sdf.format(seckillBegin), sdf.format(seckillEnd));
            return ResultGenerator.genFailResult(exposerVO);
        }
        //校验虚拟库存
        Integer stock = redisCache.getCacheObject(Constants.SECKILL_GOODS_STOCK_KEY + seckillId);
        if (stock < 1) {
            ExposerVO exposerVO = new ExposerVO(SeckillStatusEnum.STARTED_SHORTAGE_STOCK, seckillId);//库存不足
            return ResultGenerator.genFailResult(exposerVO);
        }

        //加密
        String md5 = MD5Utils.toMD5(seckillId.toString());
        ExposerVO exposerVO = new ExposerVO(SeckillStatusEnum.START, md5, seckillId);

        return ResultGenerator.genSuccessResult(exposerVO);
    }

    @Override
    public Result selectSeckillGoodDetail(Long seckillId) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分");
        SeckillGoodsVO seckillGoodsVO = redisCache.getCacheObject(Constants.SECKILL_GOODS_DETAIL + seckillId);
        if (seckillGoodsVO == null) {
            GoodsSeckill goodsSeckill = seckillMapper.selectByPrimaryKey(seckillId);
            if (goodsSeckill == null) {
                return ResultGenerator.genFailResult("秒杀id不存在");
            }
            if (!goodsSeckill.getSeckillStatus()) {// 0->false 下架
                return ResultGenerator.genFailResult("秒杀商品已下架");
            }
            seckillGoodsVO = new SeckillGoodsVO();
            BeanUtils.copyProperties(goodsSeckill, seckillGoodsVO);
            GoodsInfo goodsInfo = goodsInfoMapper.selectByPrimaryKey(goodsSeckill.getGoodsId());
            if (goodsInfo == null) {
                return ResultGenerator.genFailResult("秒杀商品不存在");
            }
            seckillGoodsVO.setGoodName(goodsInfo.getGoodsName());
            seckillGoodsVO.setGoodsIntro(goodsInfo.getGoodsIntro());
            seckillGoodsVO.setGoodsDetailContent(goodsInfo.getGoodsDetailContent());
            seckillGoodsVO.setGoodsCoverImg(goodsInfo.getGoodsCoverImg());
            seckillGoodsVO.setSellingPrice(goodsInfo.getSellingPrice());

            Date seckillBegin = goodsSeckill.getSeckillBegin();
            Date seckillEnd = goodsSeckill.getSeckillEnd();
            seckillGoodsVO.setSeckillBeginTime(sdf.format(seckillBegin));
            seckillGoodsVO.setSeckillEndTime(sdf.format(seckillEnd));
            redisCache.setCacheObject(Constants.SECKILL_GOODS_DETAIL + seckillId, seckillGoodsVO);
        }
        return ResultGenerator.genSuccessResult(seckillGoodsVO);
    }

    @Override
    public Result executeSeckill(Long seckillId, Long userId) {
        // 判断能否在500毫秒内得到令牌，如果不能则立即返回false，不会阻塞程序
        if (!rateLimiter.tryAcquire(500, TimeUnit.MILLISECONDS)) {
            return ResultGenerator.genFailResult("秒杀失败");
        }
        //判断用户是否购买过秒杀商品
        if (redisCache.catainsCacheSet(Constants.SECKILL_SUCCESS_USER_ID + seckillId, userId)) {
            return ResultGenerator.genFailResult("您已经购买过该商品");
        }
        //更新秒杀商品虚拟库存
        Long stock = redisCache.luaDecrement(Constants.SECKILL_GOODS_STOCK_KEY + seckillId);
        if (stock < 0) {
            return ResultGenerator.genFailResult("秒杀商品已售空");
        }

        //从缓存中获取秒杀商品
        GoodsSeckill goodsSeckill = redisCache.getCacheObject(Constants.SECKILL_KEY + seckillId);

        if (goodsSeckill == null) {
            goodsSeckill = seckillMapper.selectByPrimaryKey(seckillId);
            if (goodsSeckill == null) {
                return ResultGenerator.genFailResult("秒杀商品不存在");
            }
            //当商品表商品被删除时，商品秒杀表的商品就被下架 0->false 下架 seckill_status =0
            if (!goodsSeckill.getSeckillStatus()) {
                return ResultGenerator.genFailResult("秒杀商品已被下架");
            }

            //goodsInfo不存在为空的状态 除非有脏数据 goodsInfo为逻辑删除 不需要判断
//            GoodsInfo goodsInfo = goodsInfoMapper.selectByPrimaryKey(goodsSeckill.getGoodsId());
            //秒杀时商品存在时间 24h
            redisCache.setCacheObject(Constants.SECKILL_KEY + seckillId, goodsSeckill, 24, TimeUnit.HOURS);
        }
        // 判断秒杀商品是否再有效期内
        long beginTime = goodsSeckill.getSeckillBegin().getTime();
        long endTime = goodsSeckill.getSeckillEnd().getTime();
        Date now = new Date();
        long nowTime = now.getTime();
        if (nowTime < beginTime) {
            return ResultGenerator.genFailResult("秒杀还未开始");
        } else if (nowTime > endTime) {
            return ResultGenerator.genFailResult("秒杀已经结束");
        }

        Date killTime = new Date();
        HashMap<String, Object> map = new HashMap<>(8);
        map.put("seckillId", seckillId);
        map.put("userId", userId);
        map.put("killTime", killTime);
        map.put("result", null);

        try {
            seckillMapper.killGoodsByProcedure(map);
        } catch (Exception e) {
            e.printStackTrace();
            return ResultGenerator.genFailResult("秒杀异常");
        }

        //获取result -2 sql执行失败  ； -1 未插入数据 ；0 未更新数据 ; 1 sql执行成功
        map.get("result");

        Integer result = MapUtils.getInteger(map, "result", -2);

        if (result != 1) {
            return ResultGenerator.genFailResult("很遗憾！未抢购到秒杀商品");
        }

        //记录购买过的用户
        redisCache.setCacheSet(Constants.SECKILL_SUCCESS_USER_ID + seckillId, userId);

        long endExpireTime = endTime / 1000;
        long nowExpireTime = nowTime / 1000;
        redisCache.expire(Constants.SECKILL_SUCCESS_USER_ID + seckillId, endExpireTime - nowExpireTime, TimeUnit.SECONDS);
        GoodsSeckillSuccess goodsSeckillSuccess = seckillSuccessMapper.getSeckillSuccessByUserIdAndSeckillId(userId, seckillId);

        SeckillSuccessVO successVO = new SeckillSuccessVO();
        Long secId = goodsSeckillSuccess.getSecId();
        successVO.setSeckillSuccessId(secId);
        successVO.setMd5(MD5Utils.toMD5(String.valueOf(secId)));
        return ResultGenerator.genSuccessResult(successVO);
    }

    @Override
    public boolean save(GoodsSeckill goodsSeckill) {
        GoodsInfo goodsInfo = goodsInfoMapper.selectByPrimaryKey(goodsSeckill.getGoodsId());
        if (goodsInfo == null) {
            System.out.println("商品不存在");
            return false;//此处抛出异常最好  自定义异常然后抛出
        }
        goodsSeckill.setSeckillStatus(true);//true ->1  设置为上架状态
        return seckillMapper.insertSelective(goodsSeckill) > 0;
    }

    @Override
    public void hotData() {
        List<SeckillGoodsVO> seckillGoodsVOS = new ArrayList<>();
        List<SeckillGoodsVO> seckillGoodsVOSList = null;

        //初始化数据  此处应该分页查询 分批插入redis
        List<GoodsSeckill> seckillGoodsListFromDb = seckillMapper.selectHotData();
        if (!CollectionUtils.isEmpty(seckillGoodsListFromDb)) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分");

            seckillGoodsVOSList = seckillGoodsListFromDb.stream().map(goodsSeckill -> {
                SeckillGoodsVO seckillGoodsVO = new SeckillGoodsVO();
                BeanUtils.copyProperties(goodsSeckill, seckillGoodsVO);

                //校验商品是否存在
                GoodsInfo goodsInfo = goodsInfoMapper.selectByPrimaryKey(goodsSeckill.getGoodsId());
                if (goodsInfo == null) {
                    return null;
                }

                //===================list======
                seckillGoodsVO.setGoodName(goodsInfo.getGoodsName());
                seckillGoodsVO.setGoodsCoverImg(goodsInfo.getGoodsCoverImg());
                seckillGoodsVO.setSeckillPrice(goodsInfo.getSellingPrice());
                //===================list======

                //============detail===========

                seckillGoodsVO.setGoodName(goodsInfo.getGoodsName());
                seckillGoodsVO.setGoodsCoverImg(goodsInfo.getGoodsCoverImg());
                seckillGoodsVO.setSellingPrice(goodsInfo.getSellingPrice());

                seckillGoodsVO.setGoodsIntro(goodsInfo.getGoodsIntro());
                seckillGoodsVO.setGoodsDetailContent(goodsInfo.getGoodsDetailContent());
                //============detail===========


                Date seckillBeginDate = seckillGoodsVO.getSeckillBegin();
                Date seckillEndDate = seckillGoodsVO.getSeckillEnd();
                String seckillBeginStr = sdf.format(seckillBeginDate);
                String seckillEndStr = sdf.format(seckillEndDate);
                seckillGoodsVO.setSeckillBeginTime(seckillBeginStr);
                seckillGoodsVO.setSeckillEndTime(seckillEndStr);

                return seckillGoodsVO;
            }).filter(seckillGoodsVO -> seckillGoodsVO != null).collect(Collectors.toList());

            seckillGoodsVOS.addAll(seckillGoodsVOSList);
        }


        if (!CollectionUtils.isEmpty(seckillGoodsVOS)) {
            for (SeckillGoodsVO seckillGoodsVO : seckillGoodsVOS) {
                //必须先预热详情数据
                String detailKey = Constants.SECKILL_GOODS_DETAIL + seckillGoodsVO.getSeckillId();
                redisCache.setCacheObject(detailKey, seckillGoodsVO);


                //预热商品数据  商品数据不包含GoodsDetailContent和GoodsIntro  这么做虽然在redis放了两份数据，牺牲了redis空间
                // 却提高了接口查询商品列表  和 查询商品详情的性能
                String key = Constants.SECKILL_GOODS_LIST;
                seckillGoodsVO.setGoodsDetailContent(null);
                seckillGoodsVO.setGoodsIntro(null);

                redisCache.setCacheObject(key, seckillGoodsVOS);
            }


            //预热库存数据
            for (GoodsSeckill seckillDb : seckillGoodsListFromDb) {
                String stockKey = Constants.SECKILL_GOODS_STOCK_KEY + seckillDb.getSeckillId();
                int stockValue = seckillDb.getSeckillNum();

                redisCache.setCacheObject(stockKey, stockValue);
            }


            //===========================批量入数据不支持 list 舍弃=======================

//            redisTemplate.executePipelined((RedisCallback<List<SeckillGoodsVO>>) redisConnection -> {
//
//                for (SeckillGoodsVO seckillGoodsVO : seckillGoodsVOS) {
//                    //必须先预热详情数据
//                    String detailKey = Constants.SECKILL_GOODS_DETAIL + seckillGoodsVO.getSeckillId();
//                    try {
//
//                        redisConnection.set(detailKey.getBytes(Constants.UTF_ENCODING), ByteUtils.objToByteArray(seckillGoodsVO));
//                    } catch (UnsupportedEncodingException e) {
//                        e.printStackTrace();
//                    }
//                    //预热商品数据  商品数据不包含GoodsDetailContent和GoodsIntro  这么做虽然在redis放了两份数据，牺牲了redis空间
//                    // 却提高了接口查询商品列表  和 查询商品详情的性能
//                    String key = Constants.SECKILL_GOODS_LIST ;
//                    seckillGoodsVO.setGoodsDetailContent(null);
//                    seckillGoodsVO.setGoodsIntro(null);
//                    try {
//
//                        redisConnection.set(key.getBytes(Constants.UTF_ENCODING),seckillGoodsVOS);
//                    } catch (UnsupportedEncodingException e) {
//                        e.printStackTrace();
//                    }
//                }
//
//
//                //预热库存数据
//                for (GoodsSeckill seckillDb : seckillGoodsListFromDb) {
//                    String stockKey = Constants.SECKILL_GOODS_STOCK_KEY + seckillDb.getSeckillId();
//                    int stockValue = seckillDb.getSeckillNum();
//                    try {
//
//                        redisConnection.set(stockKey.getBytes(Constants.UTF_ENCODING), ByteUtils.intToByteArray(stockValue));
//                    } catch (UnsupportedEncodingException e) {
//
//                    }
//                }
//
//                return null;
//            });

            //===========================批量入数据不支持 list 舍弃=======================
        }
    }
}
